﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Xml;
using System.IO;
using System.Diagnostics;
using System.Windows.Forms;

namespace RunUOGenerator
{
    public class CodeGenerator
    {
        private StringBuilder codeFile = new StringBuilder();

        private StringBuilder Constructor = new StringBuilder();
        private StringBuilder Property = new StringBuilder();

        private Module _Module = new Module();
        private string _OutputFile = "";

        private UOItem _UOItem;

        private TabControl tabControl = new TabControl();

        public string OutputFile { get { return _OutputFile; } set { _OutputFile = value; } }
        public TabControl TabControl { get { return tabControl; } set { tabControl = value; } }
        public Module Module { get { return _Module; } set { _Module = value; } }

        private int _ConstructionCount = 0;
        private int _PropertyCount = 0;

        public CodeGenerator()
        {
        }

        /// <summary>
        /// Class constructor.
        /// </summary>
        /// <param name="module"></param>
        /// <param name="tabcontrol"></param>
        /// <param name="item"></param>
        /// <param name="outputFile"></param>
        public CodeGenerator(Module module, TabControl tabcontrol, UOItem item, string outputFile)
        {
            _Module = module;
            tabControl = tabcontrol;
            _OutputFile = outputFile;
            _UOItem = item;
        }

        /// <summary>
        /// This method begins the code generation process.
        /// </summary>
        /// <returns></returns>
        public bool GenerateScript()
        {
            Debug.WriteLine("GenerateScript called.");

            if (!GenerateOutputFile())
            {
                ///TODO: Return an error or something
                return false;
            }

                // Now that we have the script file, lets scroll through our attributes and layout.
                foreach (XmlNode attributeNode in _Module.Attributes["Attributes"].ChildNodes)
                {
                    // Make sure this is an attribute node.
                    if (attributeNode.Name.ToLower() == "attribute")
                    {
                        
                        string Location = "";
                        Location = attributeNode.Attributes["Location"].Value;

                        switch (Location.ToLower())
                        {
                            case "import":
                                BuildImport(attributeNode);
                                //ModifyCodeFile("{Import}", BuildImport(Value));
                                break;
                            case "base":
                                BuildBase(attributeNode);
                                break;
                            case "namespace":
                                BuildNamespace(attributeNode);
                                //ModifyCodeFile("{Namespace}", BuildNamespace(Value));
                                break;
                            case "property":
                                BuildProperty(attributeNode);
                               // ModifyCodeFile("{Property}", BuildProperty(GetControlValue(attributeNode), Value, Type, BaseType, GenerateVariable));
                                break;
                            case "classname":
                                BuildClassName(attributeNode);
                                //ModifyCodeFile("{ClassName}", GetControlValue(attributeNode));
                                break;
                            case "constructor":
                                BuildConstructor(attributeNode);
                                //ModifyCodeFile("{Constructor}", BuildConstructor(Value, BaseType, GetControlValue(attributeNode)));
                                break;
                        }
                    }
                }

            Debug.WriteLine("Finishing building file.");
            ModifyCodeFile("{Constructor}", Constructor.ToString());
            ModifyCodeFile("{Property}", Property.ToString());
            Debug.WriteLine("Cleaning file called.");
            CleanCodeFile();


            if (OutputCodeFile())
            {
                return true;
            }

            return true;
        }

        /// <summary>
        /// This method removes any attributes from the codefile.
        /// </summary>
        private void CleanCodeFile()
        {
            codeFile.Replace("{ClassName}", "");
            codeFile.Replace("{Namespace}", "");
            codeFile.Replace("{Private}", "");
            codeFile.Replace("{Import}", "");
            codeFile.Replace("{Function}", "");
            codeFile.Replace("{Property}", "");
            codeFile.Replace("{Constructor}", "");
            codeFile.Replace("{Base}", "");
        }

        /// <summary>
        /// This method outputs the finalized StringBuilder to the new code file.
        /// </summary>
        private bool OutputCodeFile()
        {
            Debug.WriteLine("Outputting file.");
            try
            {
                StreamWriter sWriter = new StreamWriter(_OutputFile);
                sWriter.Write(codeFile);
                sWriter.Close();
            }
            catch (Exception) { return false; }

            return true;
        }

        /// <summary>
        /// This method modifies the codefile for the Class Base and appends the VALUE attribute.
        /// </summary>
        /// <param name="attributeNode"></param>
        private void BuildBase(XmlNode attributeNode)
        {
            Debug.WriteLine("Building base...");
            string buff = "";
            buff = " : " + GetControlValue(attributeNode);
            ModifyCodeFile("{Base}", " : " + _UOItem.Base);
        }

        /// <summary>
        /// This method modifies the codefile for the Namespace and appends the VALUE attribute.
        /// </summary>
        /// <param name="attributeNode"></param>
        private void BuildNamespace(XmlNode attributeNode)
        {
            string buff = "";
            buff = attributeNode.Attributes["Value"].Value;
            ModifyCodeFile("{Namespace}", buff);
        }

        /// <summary>
        /// This method modifies the codefile for an Import and appends the VALUE attribute.
        /// </summary>
        /// <param name="attributeNode"></param>
        private void BuildImport(XmlNode attributeNode)
        {
            string buff = "";
            buff = "using " + attributeNode.Attributes["Value"].Value + ";\n";
            ModifyCodeFile("{Import}", buff);
        }

        /// <summary>
        /// This method modifies the codefile for a ClassName and appends the VALUE attribute.
        /// </summary>
        /// <param name="attributeNode"></param>
        private void BuildClassName(XmlNode attributeNode)
        {
            string buff = "";
            buff = GetControlValue(attributeNode);

            if(buff != "" || buff != null)
                ModifyCodeFile("{ClassName}", buff);
        }

        /// <summary>
        /// This method modifies the codefile for a Constructor and appends the VALUE attribute.
        /// </summary>
        /// <param name="attributeNode"></param>
        private void BuildConstructor(XmlNode attributeNode)
        {
            string buff = "";
            string value = "";
            string BaseType = "";
            string inclusion = "";

            try { BaseType = attributeNode.Attributes["BaseType"].Value; }
            catch (Exception) { }

            value = GetControlValue(attributeNode);

            // Make sure we have a value. If not do nothing.
            if (value != "" && value != null && value != "-1-")
            {
                // We will need to add tabs to make it look pretty.
                if (_ConstructionCount > 0)
                {
                    inclusion = "\t\t\t";
                }

                if (BaseType.ToLower() == "string")
                    buff = inclusion + attributeNode.Attributes["Value"].Value + " = \"" + value + "\";\n";
                else if (BaseType.ToLower() == "int")
                    buff = inclusion + attributeNode.Attributes["Value"].Value + " = " + value + ";\n";
                else if (BaseType.ToLower() == "fixed")
                    buff = inclusion + attributeNode.Attributes["Value"].Value + ";\n";
                else
                    buff = inclusion + attributeNode.Attributes["Value"].Value + "." + value + ";\n";

                _ConstructionCount++;

                // Now we add this to a string builder. Using ModifyCodeFile now does... weird stuff.
                if (buff != "" || buff != null)
                    Constructor.Append(buff);
                    
            }

            Debug.WriteLine("Contructor: " + _ConstructionCount);
        }

        /// <summary>
        /// This method modifies the codefile for a Property and appends the VALUE attribute.
        /// </summary>
        /// <param name="attributeNode"></param>
        private void BuildProperty(XmlNode attributeNode)
        {
            string Type = "";
            string Value = "";
            string Override = "";
            string GenerateVariable = "";
            string BaseType = "";
            string OverrideValue = "";

            Value = attributeNode.Attributes["Value"].Value;

            // Man, I wish there was a better way to check to see if these attributes exist...
            try { Type = attributeNode.Attributes["Type"].Value; }
            catch (Exception) { }
            try { BaseType = attributeNode.Attributes["BaseType"].Value; }
            catch (Exception) { }
            try { Override = attributeNode.Attributes["Override"].Value; }
            catch (Exception) { }
            try { GenerateVariable = attributeNode.Attributes["GenerateVariable"].Value; }
            catch (Exception) { }
            try { OverrideValue = attributeNode.Attributes["OverrideValue"].Value; }
            catch (Exception) { }

            string buff = "";
            string controlValue = "";
            string inclusion = "";

            controlValue = GetControlValue(attributeNode);

            if (controlValue != "" && controlValue != null && controlValue != "-1-")
            {
                if (_PropertyCount > 0)
                    inclusion = "\t\t";

                if (GenerateVariable.ToLower() == "false" || GenerateVariable == "")
                {
                    /// TODO: This will need to be reworked for GET/SET
                    // Create our get property
                    if (Type.ToLower() == "get")
                    {
                        if(BaseType.ToLower() == "int")
                            buff = inclusion + "public override " + BaseType + " " + Value + " { get { return " + controlValue + "; } }\n";
                        else if (BaseType.ToLower() == "string")
                            buff = inclusion + "public override " + BaseType + " " + Value + " { get { return \"" + controlValue + "\"; } }\n";
                        else
                            buff = inclusion + "public override " + BaseType + " " + Value + " { get { return " + BaseType + "." + controlValue + "; } }\n";

                        _PropertyCount++;
                        Property.AppendLine(buff);
                    }
                    // Leaving this open for a get/set.
                }
                else
                {
                    //BuildPrivateVariable(value, BaseType);
                }

            }
        }

        /// <summary>
        /// This method gets the value of the BaseTabControl by matching the attributeNode.
        /// </summary>
        /// <param name="attributeNode"></param>
        /// <returns></returns>
        private string GetControlValue(XmlNode attributeNode)
        {
            // Lets take a look at all the pages.
            foreach (TabPage tabPage in tabControl.TabPages)
            {
                // Now lets look at the controls in the page.
                foreach (Control cont in tabPage.Controls)
                {
                    if (cont is BaseTabControl)
                    {
                        BaseTabControl btc = (BaseTabControl)cont;

                        // Look through out controls and find them.
                        if (btc.Attribute == attributeNode.Attributes["ID"].Value)
                        {
                            if (btc is UpDownControl)
                            {
                                UpDownControl udc = (UpDownControl)btc;
                                return btc.Value;
                            }
                            else if (btc is CheckboxControl)
                            {
                                CheckboxControl cbc = (CheckboxControl)btc;
                                if (cbc.Checked)
                                    return cbc.Value;
                                else
                                    return "-1-";
                            }
                            else
                            {
                                return btc.Value;
                            }
                        }
                    }
                    else if (cont is GroupBox)
                    {
                        GroupBox con = (GroupBox)cont;
                        string buffVal = LoopThroughGroupBox(attributeNode, con);
                        if (buffVal != "-1-")
                            return buffVal;
                    }
                }
            }
            // If nothing is found return null for processing.
            return "";
        }

        /// <summary>
        /// This method loops through a groupbox to find control to match against the attributeNode.
        /// </summary>
        /// <param name="attributeNode"></param>
        /// <param name="control"></param>
        /// <returns></returns>
        private string LoopThroughGroupBox(XmlNode attributeNode, GroupBox control)
        {
            foreach (Control cont in control.Controls)
            {
                if (cont is BaseTabControl)
                {
                    BaseTabControl btc = (BaseTabControl)cont;

                    // Look through out controls and find them.
                    if (btc.Attribute == attributeNode.Attributes["ID"].Value)
                    {
                        return btc.Value;
                    }
                }
            }

            return "-1-";
        }

        /// <summary>
        /// This method finds the TextToFind variable in the codefile and appends on the Value variable.
        /// </summary>
        /// <param name="TextToFind"></param>
        /// <param name="Value"></param>
        private void ModifyCodeFile(string TextToFind, string Value)
        {
            // Need to make sure this thing actually exists or funky things happen.
            if (codeFile.ToString().Contains(TextToFind))
            {
                int PositionEnd = codeFile.ToString().IndexOf(TextToFind) + TextToFind.Length;

                codeFile.Insert(PositionEnd, Value);

                // Scroll through one more time to find if another isntace exists.
                ///TODO: Make this a while loop.
                if (codeFile.ToString().IndexOf(TextToFind, PositionEnd + 1) != -1)
                {
                    int NextPositionEnd = codeFile.ToString().IndexOf(TextToFind, PositionEnd + 1) + TextToFind.Length;

                    codeFile.Insert(NextPositionEnd, Value);
                }
            }
        }
        
        private bool GenerateOutputFile()
        {
            string file = Program.ExecutionLocation + "\\" + _OutputFile;

            StreamReader streamReader = new StreamReader(Program.ExecutionLocation + "\\CodeFile.txt");

            while (streamReader.Peek() != -1)
            {
                codeFile.AppendLine(streamReader.ReadLine());
            }

            streamReader.Close();

            return true;
        }
    }
}
